/*
This file is part of MMM.

MMM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

MMM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with MMM.  If not, see <http://www.gnu.org/licenses/>.
*
* @package    MMM
* @author     Andre Meixner
* @copyright  2017 High Performance Humanoid Technologies (H2T), Karlsruhe, Germany
*
*/

#ifndef __MMM_RAPIDXMLWRITER_H_
#define __MMM_RAPIDXMLWRITER_H_

#include "rapidxml.hpp"
#include "rapidxml_print.hpp"

#include <boost/enable_shared_from_this.hpp>
#include <string>
#include <fstream>

namespace MMM
{
    class RapidXMLWriter;
    typedef boost::shared_ptr<RapidXMLWriter> RapidXMLWriterPtr;

    class RapidXMLWriterNode;
    typedef boost::shared_ptr<RapidXMLWriterNode> RapidXMLWriterNodePtr;

    //! @brief Represents an xml node in the RapidXMLWriter
    class RapidXMLWriterNode : public boost::enable_shared_from_this<RapidXMLWriterNode>
    {
        friend class RapidXMLWriter;

    private:
        rapidxml::xml_document<>* document;
        rapidxml::xml_node<>* node;

        RapidXMLWriterNode(rapidxml::xml_document<>* document, rapidxml::node_type node_type)
        {
            this->document = document;
            node = document->allocate_node(node_type);
        }
        RapidXMLWriterNode(rapidxml::xml_document<>* document, rapidxml::node_type node_type, const std::string& name)
        {
            this->document = document;
            node = document->allocate_node(node_type, cloneString(name));
        }

    public:

        /*! Appends a new attribute to the current node.
            @return The current Node.*/
        RapidXMLWriterNodePtr append_attribute(const std::string& name, const std::string& value)
        {
            node->append_attribute(document->allocate_attribute(cloneString(name), cloneString(value)));
            return shared_from_this();
        }

        RapidXMLWriterNodePtr append_bool_attribute(const std::string& name, const std::string& trueValue, const std::string& falseValue, bool value)
        {
            append_attribute(name, value ? trueValue : falseValue);
            return shared_from_this();
        }

        RapidXMLWriterNodePtr append_optional_bool_attribute(const std::string& name, const std::string& trueValue, const std::string& falseValue, bool value, bool defaultValue)
        {
            if (value != defaultValue)
            {
                append_attribute(name, value ? trueValue : falseValue);
            }
            return shared_from_this();
        }

        /*! Appends a new node on the current node.
            @return The new Node.*/
        RapidXMLWriterNodePtr append_node(const std::string& name)
        {
            RapidXMLWriterNodePtr node(new RapidXMLWriterNode(document, rapidxml::node_element, name));
            this->node->append_node(node->node);
            return node;
        }

        /*! Adds string content to the current node.
            @return The current Node.*/
        RapidXMLWriterNodePtr append_data_node(const std::string& value)
        {
            this->node->append_node(document->allocate_node(rapidxml::node_data, 0, cloneString(value)));
            return shared_from_this();
        }

        RapidXMLWriterNodePtr append_string_node(const std::string& name, const std::string& value)
        {
            this->node->append_node(document->allocate_node(rapidxml::node_element, cloneString(name), cloneString(value)));
            return shared_from_this();
        }

    private:
        const char* cloneString(const std::string& str)
        {
            return document->allocate_string(str.c_str());
        }

    };

    //! @brief Helper class for converting objects to an xml format via Rapid XML
    class RapidXMLWriter
    {
    private:
        rapidxml::xml_document<> document;

    public:
        /*! Creates an RapidXMLWriter for creating an xml document */
        RapidXMLWriter()
        {
            RapidXMLWriterNodePtr declaration(new RapidXMLWriterNode(&document, rapidxml::node_declaration));
            declaration->append_attribute("version", "1.0");
            declaration->append_attribute("encoding", "utf-8");
            document.append_node(declaration->node);
        }

        /*! Creates a root node with the given name.
            @return The root Node. */
        RapidXMLWriterNodePtr createRootNode(const std::string& name)
        {
            RapidXMLWriterNodePtr rootNode(new RapidXMLWriterNode(&document, rapidxml::node_element, name));
            document.append_node(rootNode->node);
            return rootNode;
        }

        /*! Creates an xml string representation of the created xml structure.
            @param indent Usage of tabs in the string representation for better readability. */
        std::string print(bool indent)
        {
            std::string s;
            rapidxml::print(std::back_inserter(s), document, indent ? 0 : rapidxml::print_no_indenting);
            return s;
        }

        /*! Saves the created xml structure as xml document.
            @param path Path where the xml document should be saved.
            @param indent Usage of tabs in the string representation for better readability.*/
        void saveToFile(const std::string& path, bool indent)
        {
            std::ofstream file;
            file.open(path.c_str());
            file << print(indent);
            file.close();
        }


    };

    typedef boost::shared_ptr<RapidXMLWriterNode> RapidXMLWriterNodePtr;
    typedef boost::shared_ptr<RapidXMLWriter> RapidXMLWriterPtr;
}

#endif // __MMM_RAPIDXMLWRITER_H_
